
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include "Mailer.h"
#include "Message.h"
#include "Message_System.h"
#include "Message_RT.h"
#include "Message_Kill_Revive.h"

using namespace std;


void* run1(void *args)
{
	Mailer* mailer = (Mailer*)args;
	int times = 0;						// remove it and return to while true
	while( true )
	//while( times < 400000 )
	{
		// handle messages
		mailer->handle_messages();


		// scheduling
		mailer->handle_scheduling();
		times++;
	}
}


Mailer::Mailer(Mailbox* shell_inbox) : _shell_inbox(shell_inbox){
	_graph			= new Graph();
	_buffer_size	= 0;
	_mailer_inbox	= NULL;
	_scheduler		= NULL;
}

Mailer::~Mailer() {
	delete(_graph);
	delete(_mailer_inbox);
	delete(_scheduler);
}

bool Mailer::creat_net(string filename)
{
	_mailer_inbox	= new Mailbox(_buffer_size);
	bool ans = _graph->build_graph(filename, _mailer_inbox);
	_buffer_size	= _graph->get_buffer_size();
	_mailer_inbox->setBuffer_size(_buffer_size);

	// set mailer_inbox to all workers:
	for (unsigned int i = 1; i < _graph->getNodes()->size(); ++i) {
		((Worker*)_graph->getNodes()->at(i))->setMailer_inbox(_mailer_inbox);
	}


	_scheduler		= new Scheduler(_graph->getNodes(), _shell_inbox);
	return ans;

}

void Mailer::put_in_inbox(Message* message)
{
	send_message(_mailer_inbox, message);
	_scheduler->setIs_state_stabilized(false);

}

void Mailer::start(int schedule_num)
{
	_scheduler->setScheduling_algorithm(schedule_num);

	// start mailer's thread:
	pthread_t mailer_thread_id;
	if ( pthread_create(&mailer_thread_id, NULL, run1, this) != 0 )
	{
		perror("Mailer thread creation failed");
		exit(1);
	}
}


void Mailer::handle_single_message()
{
	Message* msg;

	//pthread_mutex_lock(&_mailer_inbox_empty_lock);
	msg = _mailer_inbox->get_next_message();
	//pthread_cond_signal(&_mailer_inbox_empty_cond);
	//pthread_mutex_unlock(&_mailer_inbox_empty_lock);

	Message_System*			message_System;
	Message_RT*				message_RT;
	Message_Kill_Revive*	message_kill_revive;



	if( (message_System = dynamic_cast<Message_System*>(msg)) )
	{
		message_System->print();
		if ( message_System->getTarget_id() == MAILER )
		{
			sleep(1);
			cout << "good bye. I'm not the mailer!" << endl;
			pthread_exit(NULL);
		}


		int original_source = msg->getOriginal_source_id();
		int source;

		switch ( original_source )
		{
			case SHELL:	source = message_System->getSource_id();
						message_System->setOriginal_source_id(source);
						message_System->print();
						((Worker*)get_worker(source))->put_in_inbox(message_System);
						break;
			default:	message_System->print();
						int receiver = message_System->getCurrent_target_id();
						((Worker*)get_worker(receiver))->put_in_inbox(message_System);
						break;
		}


		// Forwarding message to shell:
		string text = message_System->getSource_id() + " is sending \"" + message_System->getText() + "\" to ";
		text += message_System->getTarget_id() + " through " + message_System->getCurrent_target_id();
		Message_System* message_to_shell = new Message_System(MAILER, DUMMY, DUMMY, SHELL, text);
		send_message(_shell_inbox, message_to_shell);
	}



	else if( (message_RT = dynamic_cast<Message_RT*>(msg)) )
	{
		// Forwarding message to worker:
		int target = msg->getCurrent_target_id();
		((Worker*)get_worker(target))->put_in_inbox(msg);
	}

	else if( (message_kill_revive = dynamic_cast<Message_Kill_Revive*>(msg)) )
	{
		// Forwarding message to worker:
		int target = message_kill_revive->getCurrent_target_id();
		if ( message_kill_revive->getAction() == REVIVE )
		{
			// Forwarding message to worker:
			((Worker*)_graph->getNodes()->at(message_kill_revive->getWorker_id()))->revive();
			//_scheduler->getThreads_table()->get_worker_entry(message_kill_revive->getWorker_id())->setStatus(THREAD_READY);
			_scheduler->setGot_revive_message(true);
		}


		((Worker*)get_worker(target))->put_in_inbox(message_kill_revive);
	}
}

void Mailer::handle_messages()
{
	//cout << "____________________ Mailer ____________________" << endl;
	cout << endl << "mailer: handling messages" << endl;

	if ( _mailer_inbox->is_empty() )
	{
		while ( _scheduler->getIs_state_stabilized()  &&  _mailer_inbox->is_empty() )
		{
			//cout << "\nMailer: still waiting for messages..." << endl;
			//sleep(2);
		}
	}

	if ( !_mailer_inbox->is_empty() )
	{
		cout << "Mailer: inbox has " << _mailer_inbox->get_number_of_pending_messages() << " pending messages" << endl;
		while ( !_mailer_inbox->is_empty()  )
		{
			handle_single_message();
		}
	}
}

void Mailer::handle_scheduling()
{
	//cout << "____________________ Scheduler ____________________" << endl;
	_scheduler->schedule(SCHEDULE);
}

Worker* Mailer::get_worker(int id)
{
	return _graph->getNodes()->at(id);
}

void Mailer::send_message(Mailbox* receiver_mailbox, Message* message)
{
	receiver_mailbox->put_message(message);
}
